#include "GM3Layout.h"

#include "oclUtil.h"

#include <ctime>

#include <cmath>
#include <cstring>
#include <iostream>
#include <cstdlib>
using namespace std;
//using namespace GM3;

/*
 * ---Constants explained
 * Each graph level starts with some initial temp which limits the maximum 
 * displacement of any given node during that step. The initial temperature for 
 * the coarsest graph is initialT.
 * 
 * The temperature decays according to
 *    T(i) = lambda*T(i-1) => T(i) = lambda^i * initialT
 * where i is the number of steps taken.
 *
 * ---Ideas for determining the step number
 * 1 base it on the degree of reduction--higher degree = more steps
 * 2 automatically determine it by the number of nodes on the screen; calculate
 *   the area of a node's personal space given uniform node density of the 
 *   parent graph over a [0,1] square, and set the initial temperature 
 *   proportional to that. Stop the temperature decay (and switch to a finer 
 *   graph) when the node is no longer able to move outside an area determined 
 *   by the current coarseness level. I really like the sound of this method,
 *   but I need to do a bit more analysis before I go through the trouble of
 *   implementing it.
 */

//static graph parameters//
float GM3Layout::initialT = 1.5f; //initial temperature (maximum node displacement)
int GM3Layout::coarsestSteps = 250;
int GM3Layout::finestSteps = 30;
float GM3Layout::lambda = 0.95f; //temperature reduction factor
float GM3Layout::eta = 0.000001f; //softening factor--eliminates divide by 0
float GM3Layout::repConst = 1.0f; //node-node repulsive force constant
float GM3Layout::desLength = 0.025f; //spring desired length

//multi-level
unsigned int GM3Layout::coarseningThresh = 20; //graph size to stop coarsening at
float GM3Layout::initialTDecay = 1.f;
float GM3Layout::desLengthDecay = sqrt(7.f/4.f); //see Hu, Yifan. "Efficient..."

//multi-pole
float GM3Layout::theta = 1.2f; //threshold angle for approximation
                            //this is actually the tangent of the desired angle
float GM3Layout::err = 0.01; //1% allowed approximation errror
float GM3Layout::omega = 0.5f; //placeholder; actual value should be computed
                            //from err

//multi-timestep
float GM3Layout::correlatingConst = 10.f;


GM3Layout::GM3Layout(const char *fn):
    graphList(), currGraph(),
    currT(initialT)
{
    loadFile(fn);
}

GM3Layout::GM3Layout(GM3Graph *g):
    graphList(), currGraph(),
    currT(initialT)
{
    if(g) loadGraph(g);
}

void GM3Layout::loadFile(const char *fn){
    if(fn) loadGraph(new GM3Graph(fn));
}

GM3Layout::~GM3Layout(){}

void GM3Layout::loadGraph(GM3Graph *g, unsigned int N, unsigned int E, bool ifRandomize){
    //make sure any old graphs are cleared out
    graphList.clear();

    //load a new ground-level graph
    graphList.push_back(*g);
    //currGraph = graphList[0];
    currGraph = graphList.begin();
    if(ifRandomize){
        //cout << "test: randomize() ran" << endl;
        graphList[0].randomize();
    }
    buildReductions();
}

void GM3Layout::buildReductions(){
    //clear out any old reductions
    for(unsigned int i=graphList.size()-1; i>1; i--) graphList.pop_back();

    //check that further coarsening is both needed, and will make progress
    while( (graphList.back().numNodes() > coarseningThresh) &&
           (graphList.back().numEdges() > 0) )
        graphList.push_back( *(graphList.back().maxIndptSet()) );

    currGraph = graphList.begin();
}


void GM3Layout::randomize(){
    currGraph->randomize();
    currStep = 0;
}

void GM3Layout::randomizeAll(){
    for(unsigned int i=0; i<graphList.size(); i++) graphList[i].randomize();
}

void GM3Layout::rescaleAll(){
    for(unsigned int i=0; i<graphList.size(); i++) graphList[i].rescale();
}

void GM3Layout::buildKDTree(){
    std::vector<unsigned int> trans = currGraph->buildKDTree();
    //graphList[graphLvl].translateEdges(trans); //done in KDTree
    if(currGraph > graphList.begin()) (currGraph-1)->translateParents(trans);
}

void GM3Layout::buildKDTree(unsigned int graphLvl){
    std::vector<unsigned int> trans = graphList[graphLvl].buildKDTree();
    //graphList[graphLvl].translateEdges(trans); //done in KDTree
    if(graphLvl > 0) graphList[graphLvl-1].translateParents(trans);
}

void GM3Layout::buildKDTree(unsigned int graphLvl, unsigned int thresh){
    std::vector<unsigned int> trans = graphList[graphLvl].buildKDTree(thresh);
    //graphList[graphLvl].translateEdges(trans); //done in KDTree
    if(graphLvl > 0) graphList[graphLvl-1].translateParents(trans);
}

#ifdef TIME_ACC
double GM3Layout::tdiff(timespec t0, timespec t1){
    double dt = 0.0;

    if(t1.tv_nsec < t0.tv_nsec){
        dt += t1.tv_sec - t0.tv_sec - 1;
        dt += (double)((t1.tv_nsec + 1000000000)-t0.tv_nsec)/(double)1000000000;
    }else{
        dt += t1.tv_sec - t0.tv_sec;
        dt += (double)(t1.tv_nsec - t0.tv_nsec)/(double)1000000000;
    }

    return dt;
}
#endif
